#include "fs.h"
#include "super_block.h"
#include "inode.h"
#include "dir.h"
#include "stdint.h"
#include "stdio-kernel.h"
#include "list.h"
#include "string.h"
#include "ide.h"
#include "global.h"
#include "debug.h"
#include "memory.h"
#include "file.h"

struct partition* cur_part; // 默认情况下操作的是哪个分区

// 在分区链表中找到名为 part_name 的分区，并将其指针赋值给 cur_part
static bool mount_partition(struct list_elem* pelem, int arg) {
    char* part_name = (char*) arg;
    struct partition* part = elem2entry(struct partition, part_tag, pelem);

    if(!strcmp(part -> name, part_name)) {
        cur_part = part;
        struct disk* hd = cur_part -> my_disk;

        // 准备 super_block 结构体准备用来存储从硬盘读入的超级块数据
        struct super_block* sb_buf = (struct super_block*) sys_malloc(SECTOR_SIZE);

        // 在内存中创建分区 cur_part 的超级块
        cur_part -> sb = (struct super_block*) sys_malloc(sizeof(struct super_block));

        if(cur_part -> sb == NULL)
            PANIC("alloc memory failed!");
        
        // 读入超级块
        memset(sb_buf, 0, SECTOR_SIZE);
        ide_read(hd, cur_part -> start_lba + 1, sb_buf, 1);

        // 把 sb_buf 中的超级块信息复制到分区的超级块 sb 中
        memcpy(cur_part -> sb, sb_buf, sizeof(struct super_block));

        /*---------------------------------------*/
        // 将硬盘上的块位图读入到内存
        /*---------------------------------------*/
        cur_part -> block_bitmap.bits = (uint8_t*) sys_malloc(sb_buf -> block_bitmap_sects * SECTOR_SIZE);
        if(cur_part -> block_bitmap.bits == NULL)
            PANIC("alloc memory failed!");
        cur_part -> block_bitmap.btmp_bytes_len = sb_buf -> block_bitmap_sects * SECTOR_SIZE;
        // 从硬盘上读入块位图到分区的 block_bitmap.bits
        ide_read(hd, sb_buf -> block_bitmap_lba, cur_part -> block_bitmap.bits, sb_buf -> block_bitmap_sects);

        /*---------------------------------------*/
        // 将硬盘上的 inode 位图读入到内存
        /*---------------------------------------*/
        cur_part -> inode_bitmap.bits = (uint8_t*) sys_malloc(sb_buf -> inode_bitmap_sects * SECTOR_SIZE);
        if(cur_part -> inode_bitmap.bits == NULL)
            PANIC("alloc memroy failed!");
        cur_part -> inode_bitmap.btmp_bytes_len = sb_buf -> inode_bitmap_sects * SECTOR_SIZE;
        // 从硬盘上读入 inode 位图到分区的 inode_bitmap.bits
        ide_read(hd, sb_buf -> inode_bitmap_lba, cur_part -> inode_bitmap.bits, sb_buf -> inode_bitmap_sects);

        // ------------
        list_init(&cur_part -> open_inodes);
        printk("mount %s done!\n", part -> name);

     /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
        只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        return true;
    }
    return false; // 使 list_traversal 继续遍历
}

// 格式化分区，也就是初始化分区中的元信息，从而创建文件系统
static void partition_format(struct partition* part) {
    // 为了方便，一个块大小 = 一个扇区
    uint32_t boot_sector_sects = 1;
    uint32_t super_block_sects = 1;
    // inode 位图所占用的扇区数，最多支持 4096 个文件
    // 目前我理解为：512byte = 4096bit, 即每个 bit 对应一个“块”, 每个块可以是 inode
    uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);
    // inode_table 所需要的扇区数
    uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);
    // 已使用的扇区数
    uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;
    // 空闲空间的扇区数
    uint32_t free_sects = part -> sec_cnt - used_sects;

    // 块的位图 所占用的扇区数
    // 一个块 = 一个扇区, 4096 位 = 4096 个块, 直接除于 4096 可得到扇区数
    uint32_t block_bitmap_sects;
    block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);
    // 位图中位的个数
    uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects;
    block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);

    /*------------------------------------*/
    // super_block 超级块的初始化
    /*------------------------------------*/
    struct super_block sb;
    sb.magic = 0x19590318;
    sb.sec_cnt = part -> sec_cnt;
    sb.inode_cnt = MAX_FILES_PER_PART;
    sb.part_lba_base = part -> start_lba;

    sb.block_bitmap_lba = sb.part_lba_base + 2; // 第 0 块是引导块，第 1 块是超级块
    sb.block_bitmap_sects = block_bitmap_sects;

    sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;
    sb.inode_bitmap_sects = inode_bitmap_sects;

    sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;
    sb.inode_table_sects = inode_table_sects;

    sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;
    sb.root_inode_no = 0;
    sb.dir_entry_size = sizeof(struct dir_entry);

    printk("%s info:\n", part->name);
    printk("   magic:0x%x\n   part_lba_base:0x%x\n   all_sectors:0x%x\n   inode_cnt:0x%x\n   block_bitmap_lba:0x%x\n   block_bitmap_sectors:0x%x\n   inode_bitmap_lba:0x%x\n   inode_bitmap_sectors:0x%x\n   inode_table_lba:0x%x\n   inode_table_sectors:0x%x\n   data_start_lba:0x%x\n", sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);

    /*-----------------------------------------*/
    // 将超级块写入本分区的 1 扇区（0 扇区是引导扇区）
    /*-----------------------------------------*/
    struct disk* hd = part -> my_disk;
    ide_write(hd, part -> start_lba + 1, &sb, 1);

    printk("   super_block_lba:0x%x\n", part->start_lba + 1);

    // 找出数据量最大的元信息，将其作为存储缓冲区
    uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);
    buf_size = (buf_size >= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;
    uint8_t* buf = (uint8_t*) sys_malloc(buf_size);

    /*-----------------------------------------*/
    // 将块位图初始化并写入 sb.block_bitmap_lba
    /*-----------------------------------------*/
    buf[0] |= 0x01; // 第 0 块先预留给根目录，位图先占位
    uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8; // 位图的长度，以字节为单位
    uint8_t block_bitmap_last_bit = block_bitmap_bit_len % 8; // 最后不足一个字节的位的个数
    // last_size 是位图所在扇区最后不足一扇区的部分
    uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);

    // 将位图最后一个字节所在扇区的有效位全部置为 1, 也就是说超出的那 last_size 全部设置为已占用状态
    memset(&buf[block_bitmap_last_byte], 0xFF, last_size);

    // 将最后一个字节中其有效的部分将其置为 0，也就是没有超出的那部分
    uint8_t bit_idx = 0;
    while(bit_idx <= block_bitmap_last_bit)
        buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
    ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);

    /*--------------------------------------------*/
    // 将 inode 位图初始化并写入 sb.inode_bitmap_lba
    /*--------------------------------------------*/
    // 先清空缓冲区
    memset(buf, 0, buf_size);
    buf[0] |= 0x01; // 第 0 个 inode 分给了根目录
    /**
     * 因为 inode_table 共 4096 个 inode，inode_bitmap 位图正好占用一个扇区
     * 即 inode_bitmap_sects = 1
     * 所以位图中的所有扇区均是有效位，无需再像 block_bimap 那样处理残余的扇区
     */
    ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);

    /*--------------------------------------------*/
    // 将 inode 数组初始化并写入 sb.inode_table_lba
    /*--------------------------------------------*/
    memset(buf, 0, buf_size);
    // 初始化第一个 inode 项，它表示根目录
    struct inode* i = (struct inode*) buf; 
    i -> i_size = sb.dir_entry_size * 2; // . 和 ..
    i -> i_no = 0; // inode 编号
    i -> i_sectors[0] = sb.data_start_lba; // 第 0 个块指向 sb.data_start_lba
                                           // 也就是将根目录安排在最开始的空闲块中
    ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);

    /*--------------------------------------------*/
    // 将根目录初始化并写入 sb.data_start_lba
    /*--------------------------------------------*/
    // 写入目录项 . 和 ..
    memset(buf, 0, buf_size);
    struct dir_entry* p_de = (struct dir_entry*) buf;
    // 初始化当前目录 .
    memcpy(p_de -> filename, ".", 1);
    p_de -> i_no = 0;
    p_de -> f_type = FT_DIRECTORY;
    p_de++;

    // 初始化当前目录的父目录 ..
    memcpy(p_de -> filename, "..", 2);
    p_de -> i_no = 0; // 根父目录还是自己
    p_de -> f_type = FT_DIRECTORY;

    ide_write(hd, sb.data_start_lba, buf, 1);

    printk("   root_dir_lba:0x%x\n", sb.data_start_lba);
    printk("%s format done\n", part->name);
    sys_free(buf);
}

// 将最顶层的路径名称解析出来
static char* path_parse(char* pathname, char* name_store) {
    if(pathname[0] == '/') { // 根目录不单独解析
        /* 路径中出现1个或多个连续的字符'/',将这些'/'跳过,如"///a/b" */
        while(*(++pathname) == '/');
    }

    // 一般路径解析
    while(*pathname != '/' && *pathname != 0)
        *name_store++ = *pathname++;
    
    if(pathname[0] == 0)
        return NULL;
    
    return pathname;
}

// 返回路径深度
int32_t path_depth_cnt(char* pathname) {
    ASSERT(pathname != NULL);
    char* p = pathname;
    char name[MAX_FILE_NAME_LEN];
    uint32_t depth = 0;

    // 解析路径，从中拆分出各级名称
    p = path_parse(p, name);
    while(name[0]) {
        depth++;
        memset(name, 0, MAX_FILE_NAME_LEN);
        if(p) // 若 p != NULL，则继续解析
            p = path_parse(p, name);
    }

    return depth;
}

// 搜索文件
static int search_file(const char* pathname, struct path_search_record* searched_record) {
    // 若待查找的是根目录，直接返回
    if (!strcmp(pathname, "/") || !strcmp(pathname, "/.") || !strcmp(pathname, "/..")) {
        searched_record->parent_dir = &root_dir;
        searched_record->file_type = FT_DIRECTORY;
        searched_record->searched_path[0] = 0;	   // 搜索路径置空
        return 0;
    }

    uint32_t path_len = strlen(pathname);
    /* 保证pathname至少是这样的路径/x且小于最大长度 */
    ASSERT(pathname[0] == '/' && path_len > 1 && path_len < MAX_PATH_LEN);
    char* sub_path = (char*) pathname;
    struct dir* parent_dir = &root_dir;
    struct dir_entry dir_e;

    char name[MAX_FILE_NAME_LEN] = {0};

    searched_record -> parent_dir = parent_dir;
    searched_record -> file_type = FT_UNKNOWN;
    uint32_t parent_inode_no = 0; // 父目录的 inode 编号

    sub_path = path_parse(sub_path, name);
    while(name[0]) { // 路径不许为空
        /* 记录查找过的路径,但不能超过searched_path的长度512字节 */
        ASSERT(strlen(searched_record->searched_path) < 512);

        // 记录已存在的父目录
        strcat(searched_record -> searched_path, "/");
        strcat(searched_record -> searched_path, name);

        if(search_dir_entry(cur_part, parent_dir, name, &dir_e)) { // 在所给的目录中查找文件
            memset(name, 0, MAX_FILE_NAME_LEN);
            if(sub_path) // 继续解析路径
                sub_path = path_parse(sub_path, name);
            if(FT_DIRECTORY == dir_e.f_type) { // 被打开的是目录
                parent_inode_no = parent_dir -> inode -> i_no;
                dir_close(parent_dir);
                parent_dir = dir_open(cur_part, dir_e.i_no); // 更新父目录
                searched_record -> parent_dir = parent_dir;
                continue;
            } else if(FT_REGULAR == dir_e.f_type) { // 被打开的是普通文件
                searched_record -> file_type = FT_REGULAR;
                return dir_e.i_no;
            }
        } else { // 找不到
            /**
             * 找不到目录项时，要留着 parent_dir 不要关闭
             * 因为若需要创建文件的话，需要在 parent_dir 中创建
             */
            return -1;
        }
    }

    // ------------------------------------------
    // 执行到这里，可说明：
    // 1. 路径 pathname 已经被完整的解析过了，各级都存在
    // 2. pathname 的最后一层路径不是普通文件，而是目录
    // ------------------------------------------
    dir_close(searched_record -> parent_dir);

    searched_record -> parent_dir = dir_open(cur_part, parent_inode_no);
    searched_record -> file_type = FT_DIRECTORY;
    return dir_e.i_no;
}

// 打开或创建文件成功后，返回文件描述符
int32_t sys_open(const char* pathname, uint8_t flags) {
    if(pathname[strlen(pathname)] == '/') {
        printk("can`t open a directory %s\n",pathname);
        return -1;
    }

    ASSERT(flags <= 7);

    int32_t fd = -1; // 默认找不到

    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));

    // 记录目录深度，便于判断中间某个目录不存在的情况
    uint32_t pathname_depth = path_depth_cnt((char*) pathname);

    // 先检查文件是否存在
    int inode_no = search_file(pathname, &searched_record);
    bool found = inode_no != -1 ? true : false;

    if(searched_record.file_type == FT_DIRECTORY) {
        printk("can`t open a direcotry with open(), use opendir() to instead\n");
        dir_close(searched_record.parent_dir);
        return -1;
    }

    uint32_t path_search_depth = path_depth_cnt(searched_record.searched_path);

    // 判断是否把 pathname 各层目录都访问到了，即是否出现目录/文件找不到的情况
    if(pathname_depth != path_search_depth) {
        printk("cannot access %s: Not a directory, subpath %s is`t exist\n", \
	     pathname, searched_record.searched_path);
        dir_close(searched_record.parent_dir);
        return -1;
    }

    if(!found && !(flags & O_CREAT)) { // 找不到目标文件，且并不是要创建文件
        printk("in path %s, file %s is`t exist\n", \
	     searched_record.searched_path, \
	     (strrchr(searched_record.searched_path, '/') + 1));
        dir_close(searched_record.parent_dir);
        return -1;
    } else if(found && (flags & O_CREAT)) { // 找到目标文件，且是创建文件操作，表示：创建的文件已存在
        printk("%s has already exist!\n", pathname);
        dir_close(searched_record.parent_dir);
        return -1;
    }

    switch(flags & O_CREAT) {
        case O_CREAT:
            printk("creating file.\n");
            fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
            dir_close(searched_record.parent_dir);
        // 其余为打开文件
    }

    return fd; // 此 fd 并非全局文件表 file_table 的下标，而是进程 PCB -> fd_table 的下标
}

// 初始化文件系统
void filesys_init() {
    uint8_t channel_no = 0, dev_no = 0, part_idx = 0;

    // sb_buf 用来存储从硬盘上读入的超级块
    struct super_block* sb_buf = (struct super_block*) sys_malloc(SECTOR_SIZE);

    if(sb_buf == NULL) 
        PANIC("alloc memory failed!");
    printk("searching filesystem...\n");

    while(channel_no < channel_cnt) { // 遍历通道
        dev_no = 0;
        while(dev_no < 2) { // 
            if(dev_no == 0) {
                dev_no++;
                continue;
            }
            struct disk* hd = &channels[channel_no].devices[dev_no];
            struct partition* part = hd -> prim_parts;
            while(part_idx < 12) { // 4个主分区 + 8个逻辑分区
                if(part_idx == 4) // 主分区遍历完了，开始切换到逻辑分区
                    part = hd -> logic_parts;
                if(part -> sec_cnt != 0) { // 判断该分区是否存在
                    memset(sb_buf, 0, SECTOR_SIZE);
                    // 读取分区的超级块，根据魔数是否正确来判断是否存在文件系统
                    ide_read(hd, part -> start_lba + 1, sb_buf, 1);
                    // 若该分区已存在文件系统，则不再进行格式化
                    if(sb_buf -> magic == 0x19590318) {
                        printk("%s had filesystem.\n", part -> name);
                    } else {
                        printk("formatting %s's partition %s...\n", hd -> name, part -> name);
                        partition_format(part);
                    }
                }
                part_idx++;
                part++; // 下一个分区
            }
            dev_no++; // 下一个硬盘
        }
        channel_no++; // 下一个通道
    }
    sys_free(sb_buf);

    // 确定默认操作的分区
    char default_part[8] = "sdb1";
    // 挂载分区
    list_traversal(&partition_list, mount_partition, (int) default_part);

    // 将当前分区的根目录打开
    open_root_dir(cur_part);

    // 初始化文件表
    uint32_t fd_idx = 0;
    while(fd_idx < MAX_FILE_OPEN) {
        file_table[fd_idx++].fd_inode = NULL;
    }
}
